/**
 * @(#)Data.java
 *
 * Copyright: Copyright (c) 2003 Carnegie Mellon University
 *
 * This program is the implementation of the original system in the Assignment 2 of "Architectures
 * of Software Systems" class. For the details of each classes, please refer to the provided javadoc
 * documents. You will also find some useful information such as implementation decisions, coding
 * standards, and naming conventions in the README file.
 *
 * History:
 *
 * Date          Author              Change
 * ----------    ----------------    ---------------------------------------------------------------
 * 07/15/2003    Jung Soo Kim        Initial version.
 */


import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.rmi.Naming;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.ArrayList;


/**
 * A data node provides access to student and course data including reading the record information
 * and writing registration information. Note that current version of data node doesn't support
 * writing student and course records to files. A shutdown means loss of all registration
 * information.
 */
public class Data extends UnicastRemoteObject implements RIData {

    private static final String DATA_NAME = "Data";

    /**
     * A list of <code>Student</code> objects containing student records.
     */
    protected ArrayList vStudent;

    /**
     * A list of <code>Course</code> objects containing course records.
     */
    protected ArrayList vCourse;

    /**
     * Construct a data node that provides access to student and course data. Initial data are
     * filled with the records in the given student and course record files. At the time of
     * creation, the data node does not contain any registration information.
     *
     * @param sStudentFileName the name of student record file
     * @param sCourseFileName the name of course record file
     */
    public Data(String sStudentFileName, String sCourseFileName)
           throws RemoteException, FileNotFoundException, IOException {
        // Open the given student and course files.
        BufferedReader objStudentFile = new BufferedReader(new FileReader(sStudentFileName));
        BufferedReader objCourseFile  = new BufferedReader(new FileReader(sCourseFileName));

        // Initialize student and course lists.
        this.vStudent = new ArrayList();
        this.vCourse  = new ArrayList();

        // Populate student and course lists.
        while (objStudentFile.ready()) {
            this.vStudent.add(new Student(objStudentFile.readLine()));
        }
        while (objCourseFile.ready()) {
            this.vCourse.add(new Course(objCourseFile.readLine()));
        }

        // Close the student and course files.
        objStudentFile.close();
        objCourseFile.close();
    }

    /**
     * Return all student records as a list.
     *
     * @return an <code>ArrayList</code> of <code>Student</code> objects containing student records
     */
    public ArrayList getAllStudentRecords() {
        // Return the student list as it is.
        return this.vStudent;
    }

    /**
     * Return all course records as a list.
     *
     * @return an <code>ArrayList</code> of <code>Course</code> objects containing course records
     */
    public ArrayList getAllCourseRecords() {
        // Return the course list as it is.
        return this.vCourse;
    }

    /**
     * Return a student record whose ID is equal to the given student ID.
     *
     * @param  sSID a student ID to lookup
     * @return a <code>Student</code> object whose ID is equal to <code>sSID</code>.
     *         <code>null</code> if not found
     * @see    #getStudentName(String)
     */
    public Student getStudentRecord(String sSID) {
        // Lookup and return the matching student record if found.
        for (int i=0; i<this.vStudent.size(); i++) {
            Student objStudent = (Student) this.vStudent.get(i);
            if (objStudent.match(sSID)) {
                return objStudent;
            }
        }

        // Return null if not found.
        return null;
    }

    /**
     * Return the name of a student whose ID is equal to the given student ID.
     *
     * @param  sSID a student ID to lookup
     * @return a <code>String</code> representing student name. <code>null</code> if not found
     * @see    #getStudentRecord(String)
     */
    public String getStudentName(String sSID) {
        // Lookup and return the matching student name if found.
        for (int i=0; i<this.vStudent.size(); i++) {
            Student objStudent = (Student) this.vStudent.get(i);
            if (objStudent.match(sSID)) {
                return objStudent.getName();
            }
        }

        // Return null if not found.
        return null;
    }

    /**
     * Return a course record whose ID is equal to the given course ID.
     *
     * @param  sCID a course ID to lookup
     * @param  sSection a course section to lookup
     * @return a <code>Course</code> object whose ID is equal to <code>sCID</code>.
     *         <code>null</code> if not found
     * @see    #getCourseName(String)
     */
    public Course getCourseRecord(String sCID, String sSection) {
        // Lookup and return the matching course record if found.
        for (int i=0; i<this.vCourse.size(); i++) {
            Course objCourse = (Course) this.vCourse.get(i);
            if (objCourse.match(sCID, sSection)) {
                return objCourse;
            }
        }

        // Return null if not found.
        return null;
    }

    /**
     * Return the name of a course whose ID is equal to the given course ID.
     *
     * @param  sCID a course ID to lookup
     * @return a <code>String</code> representing course name. <code>null</code> if not found
     * @see    #getCourseRecord(String,String)
     */
    public String getCourseName(String sCID) {
        // Lookup and return the matching course name if found.
        for (int i=0; i<this.vCourse.size(); i++) {
            Course objCourse = (Course) this.vCourse.get(i);
            if (objCourse.match(sCID)) {
                return objCourse.getName();
            }
        }

        // Return null if not found.
        return null;
    }

    /**
     * Make a registration. No conflict check is done before updating the database. Nothing happens
     * if there is no matching student record and/or course record.
     *
     * @param  sSID a student ID to register
     * @param  sCID a course ID to register
     * @param  sSection a course section to register
     */
    public void makeARegistration(String sSID, String sCID, String sSection) {
        // Find the student record and the course record.
        Student objStudent = this.getStudentRecord(sSID);
        Course  objCourse  = this.getCourseRecord(sCID, sSection);

        // Make a registration.
        if (objStudent != null && objCourse != null) {
            objStudent.registerCourse(objCourse);
            objCourse.registerStudent(objStudent);
        }
    }

    /**
     * Creates a data node and starts it. Three parameters are expected, the first one as student
     * file name, the second one as course file name, and the third one as data node name.
     *
     * @param args array of input parameters
     */
    public static void main(String args[]) {
        // Check the number of parameters.
        if (args.length != 2) {
            System.err.println("Incorrect number of parameters");
            System.err.println("Usage: java Data <StudentFile> <CourseFile>");
            System.exit(1);
        }

        // Check if input files exists.
        if (!new File(args[0]).exists()) {
            System.err.println("Could not find " + args[0]);
            System.exit(1);
        }
        if (new File(args[1]).exists() == false) {
            System.err.println("Could not find " + args[1]);
            System.exit(1);
        }

        try {
            // Create a data node and make it available through RMI.
            Data objData = new Data(args[0], args[1]);
            Naming.bind(DATA_NAME, objData);
            System.out.println("Data node is ready to serve.");

            // Wait for user interrupt.
            System.out.println("Press Enter to terminate.");
            System.in.read();

            // Clean up and finish.
            Naming.unbind(DATA_NAME);
            System.out.println("Data node is leaving, bye. Press ctrl-c if the delay is too long.");
        }
        catch (java.rmi.ConnectException e) {
            // Show error message and quit.
            System.err.println("Java RMI error: check if rmiregistry is running.");
            System.exit(1);
        }
        catch (Exception e) {
            // Dump the exception information for debugging.
            System.err.println("Unexpected exception at " + DATA_NAME);
            e.printStackTrace();
            System.exit(1);
        }
    }
}
